/*
 * Copyright 2010-2011 CGT&DC ISA RAS
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package websolve.workflow.wfms;

import java.io.File;
import java.io.FileInputStream;
import java.security.KeyStore;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.logging.Logger;

import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;

import org.codehaus.jettison.json.JSONObject;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.server.handler.ResourceHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.xml.XmlConfiguration;
import org.eclipse.jetty.xml.XmlParser;
import org.eclipse.jetty.xml.XmlParser.Node;

import websolve.everest.Container;
import websolve.everest.security.AccessPolicy;
import websolve.everest.server.JettyContainer;
import websolve.everest.util.SSLUtilities;
import websolve.workflow.fileupload.UploadServlet;
import websolve.workflow.util.ErrorUtils;
import websolve.workflow.util.FileUtils;
import websolve.workflow.util.NetworkUtils;
import websolve.workflow.wfms.security.AuthAPIServlet;
import websolve.workflow.wfms.security.AuthzManager;

import com.sun.jersey.api.client.config.ClientConfig;
import com.sun.jersey.api.client.config.DefaultClientConfig;
import com.sun.jersey.api.core.ResourceConfig;
import com.sun.jersey.client.urlconnection.HTTPSProperties;
import com.sun.jersey.spi.container.servlet.ServletContainer;

public class Server {

    public static void main(String[] args) throws Exception {

        if (args.length == 0) {
            System.out.println("Usage: websolve.workflow.wfms.Server jetty-xml-config");
            System.exit(1);
        }

        org.eclipse.jetty.server.Server jetty = null;

        try {

            log.info("Starting WfMS...");

            // jetty
            Resource jetty_xml = Resource.newResource(args[0]);
            XmlConfiguration configuration = new XmlConfiguration(jetty_xml.getInputStream());
            jetty = (org.eclipse.jetty.server.Server) configuration.configure();
            ContextHandlerCollection contexts = new ContextHandlerCollection();
            jetty.setHandler(contexts);

            String host = NetworkUtils.getLocalAddress().getCanonicalHostName();
            int port = jetty.getConnectors()[0].getPort();
            boolean secure = jetty.getConnectors()[0].getClass().getSimpleName().startsWith("Ssl");
            String baseURI = (secure ? "https" : "http") + "://" + host + ":" + port;

            // use the same SSL settings for HTTPS client (ServiceTask)
            if (secure) {
                XmlParser parser = new XmlParser();
                Node root = parser.parse(new File(args[0]));
                Iterator<Node> sets = root.get("Call").get("Arg").get("New").iterator("Set");
                Map<String, String> params = new HashMap<String, String>();
                while (sets.hasNext()) {
                    Node node = sets.next();
                    String param = node.getAttribute("name");
                    String value = (String) node.get(0);
                    System.out.println(param + " = " + value);
                    params.put(param, value);
                }

                KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
                KeyStore ks = KeyStore.getInstance("JKS");
                ks.load(new FileInputStream(new File(params.get("Keystore"))), params.get("Password").toCharArray());
                kmf.init(ks, params.get("KeyPassword").toCharArray());

                SSLContext ctx = SSLContext.getInstance("TLS");
                KeyManager[] km = kmf.getKeyManagers();
                TrustManager[] tm = new TrustManager[] { new SSLUtilities.FakeX509TrustManager() };
                ctx.init(km, tm, null);

                ClientConfig config = new DefaultClientConfig();
                config.getProperties().put(HTTPSProperties.PROPERTY_HTTPS_PROPERTIES,
                        new HTTPSProperties(new SSLUtilities.FakeHostnameVerifier(), ctx));

                ServiceTask.setClientConfig(config);
            }

            // wfms config
            JSONObject wfmsConfig = new JSONObject(new String(FileUtils.readFile("conf/wfms.conf")));
            String wfDir = wfmsConfig.getString("workflow-dir");
            File wfDirF = new File(wfDir);
            if (!wfDirF.exists()) {
                wfDirF.mkdirs();
            }
            log.info("Workflow storage dir: " + wfDirF.getAbsolutePath());
            String uploadDir = wfmsConfig.getString("upload-dir");
            File uploadDirF = new File(uploadDir);
            // websolve.everest.util.FileUtils.deleteDir(uploadDirF);
            uploadDirF.mkdirs();
            log.info("File upload dir: " + uploadDirF.getAbsolutePath());

            // Everest
            Container everest = JettyContainer.setup("conf/everest.conf", null, "www");
            contexts.addHandler(everest.getServletCtxHandler());
            contexts.addHandler(everest.getWwwCtxHandler());
            
            // set session cookie with port number
            everest.getServletCtxHandler().getSessionHandler()
                    .getSessionManager().setSessionCookie("JSESSIONID:" + port);

            // WorkflowManager
            WorkflowManager wfMan = new WorkflowManager(wfDir, everest.getServiceManager(), baseURI);
            
            // Security
            AccessPolicy accessPolicy = 
                    new AccessPolicy(new File("conf/allow"), new File("conf/deny"));
            AuthzManager authzMan = new AuthzManager(accessPolicy, wfMan);

            // WfMS additional contexts
            // ContextHandler webUICtxHandler = new ContextHandler("/ui");
            // ResourceHandler webUIResHandler = new ResourceHandler();
            // webUIResHandler.setResourceBase(webuiDir);
            // webUICtxHandler.setHandler(webUIResHandler);
            // //contexts.addHandler(webUICtxHandler);

            // ServletContextHandler jerseyCtxHandler = new
            // ServletContextHandler(contexts, "/wf",
            // ServletContextHandler.SESSIONS);
            ServletHolder wfmsHolder = new ServletHolder(ServletContainer.class);
            wfmsHolder.setInitParameter("com.sun.jersey.config.property.resourceConfigClass",
                    "com.sun.jersey.api.core.PackagesResourceConfig");
            wfmsHolder.setInitParameter("com.sun.jersey.config.property.packages", "websolve.workflow.wfms");
            wfmsHolder.setInitParameter(ResourceConfig.PROPERTY_CONTAINER_REQUEST_FILTERS,
                    "websolve.everest.security.Authenticator");
            everest.getServletCtxHandler().addServlet(wfmsHolder, "/wf/*");
            everest.getServletCtxHandler().setAttribute("workflow-manager", wfMan);
            everest.getServletCtxHandler().setAttribute("authz-manager", authzMan);

            // Auth API
            ServletHolder authHolder = new ServletHolder(AuthAPIServlet.class);
            everest.getServletCtxHandler().addServlet(authHolder, "/auth");
            everest.getServletCtxHandler().addServlet(authHolder, "/auth/");
            
            ContextHandler uploadCtxHandler = new ContextHandler("/upload");
            ResourceHandler uploadResHandler = new ResourceHandler();
            uploadResHandler.setResourceBase(uploadDir);
            uploadCtxHandler.setHandler(uploadResHandler);
            contexts.addHandler(uploadCtxHandler);
            
            ServletHolder uploaderHolder = new ServletHolder(UploadServlet.class); 
            everest.getServletCtxHandler().addServlet(uploaderHolder, "/uploader/*");
            everest.getServletCtxHandler().setAttribute("upload-dir", uploadDir);

            ContextHandler logCtxHandler = new ContextHandler("/log");
            ResourceHandler logResHandler = new ResourceHandler();
            logResHandler.setResourceBase("log");
            logResHandler.setDirectoriesListed(true);
            logCtxHandler.setHandler(logResHandler);
            // contexts.addHandler(logCtxHandler);

            // editor security (see
            // http://dev.eclipse.org/svnroot/rt/org.eclipse.jetty/jetty/trunk/example-jetty-embedded/src/main/java/org/eclipse/jetty/embedded/SecuredHelloHandler.java
            // )
            // HashLoginService loginService = new
            // HashLoginService("WfMS Realm", "conf/users.conf");
            // jetty.addBean(loginService);

            // ConstraintSecurityHandler security = new
            // ConstraintSecurityHandler();
            // contexts.addHandler(security);
            // security.setHandler(webUICtxHandler);
            //
            // Constraint constraint = new Constraint();
            // constraint.setName("auth");
            // constraint.setAuthenticate( true );
            // constraint.setRoles(new String[]{"user"});
            //
            // ConstraintMapping mapping = new ConstraintMapping();
            // mapping.setPathSpec( "/*" );
            // mapping.setConstraint( constraint );
            //
            // Set<String> knownRoles = new HashSet<String>();
            // knownRoles.add("user");
            //
            // security.setConstraintMappings(Collections.singletonList(mapping),
            // knownRoles);
            // security.setAuthenticator(new BasicAuthenticator());
            // security.setLoginService(loginService);
            // security.setStrict(false);

            // // log security
            // ConstraintSecurityHandler logSecurity = new
            // ConstraintSecurityHandler();
            // contexts.addHandler(logSecurity);
            // logSecurity.setHandler(logCtxHandler);
            //
            // Constraint logConstraint = new Constraint();
            // logConstraint.setName("auth");
            // logConstraint.setAuthenticate( true );
            // logConstraint.setRoles(new String[]{"admin"});
            //
            // ConstraintMapping logMapping = new ConstraintMapping();
            // logMapping.setPathSpec( "/*" );
            // logMapping.setConstraint( logConstraint );
            //
            // Set<String> logKnownRoles = new HashSet<String>();
            // logKnownRoles.add("admin");
            //
            // logSecurity.setConstraintMappings(Collections.singletonList(logMapping),
            // logKnownRoles);
            // logSecurity.setAuthenticator(new BasicAuthenticator());
            // logSecurity.setLoginService(loginService);
            // logSecurity.setStrict(false);

            // start
            jetty.start();
            log.info("Jetty is started on port " + jetty.getConnectors()[0].getPort());
            log.info("WfMS is started");
            log.info("WebUI: " + baseURI);

        } catch (Exception e) {
            log.severe(ErrorUtils.stackTraceToString(e));

            if (jetty != null) {
                log.info("Shutting down Jetty");
                jetty.stop();
            }
        }
    }

    private static final Logger log = Logger.getLogger(Server.class.getName());
}
